//@author A0100976E
package tasky.logic;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.joda.time.DateTime;

import tasky.commons.Task;
import tasky.history.History;
import tasky.history.ShowHistory;
import tasky.storage.Storage;

public class Logic {

	private ArrayList<Task> tasks = new ArrayList<Task>();
	Storage storage;
	History history;

	private static final String ERRORMSG_INVALID_COMMAND = "Command is invalid\n";

	// messages for export
	private static final String EXPORT_SUCCESS = "Export succeeded\n";
	private static final String EXPORT_FAIL = "Export failed\n";

	// messages for import
	private static final String IMPORT_SUCCESS = "Import succeeded\n";
	private static final String IMPORT_FAIL = "Import failed\n";

	// messages for displaying today's tasks
	private static final String TODAY = "-today";

	// magic strings
	private static final String EMPTY = "";
	private static final String SPACING = " ";
	private static final String DEFAULT_CONDITION = "DEFAULT";

	// messages for command
	private static final String COMMAND_ADD = "add";
	private static final String COMMAND_CREATE = "create";
	private static final String COMMAND_DELETE = "delete";
	private static final String COMMAND_DEL = "del";
	private static final String COMMAND_EDIT = "edit";
	private static final String COMMAND_DONE = "done";
	private static final String COMMAND_UNDONE = "undone";
	private static final String COMMAND_SEARCH = "search";
	private static final String COMMAND_FIND = "find";
	private static final String COMMAND_DISPLAY = "display";
	private static final String COMMAND_SHOW = "show";
	private static final String COMMAND_HISTORY = "history";
	private static final String COMMAND_UNDO = "undo";
	private static final String COMMAND_EXIT = "exit";
	private static final String COMMAND_EXPORT = "export";
	private static final String COMMAND_IMPORT = "import";
	private static final String COMMAND_SAVE = "save";
	private static final String COMMAND_REDO = "redo";
	private static final String COMMAND_QUIT = "quit";

	// Logger
	private static Logger logger = null;
	private static final String LOGIC_LOG_FILE = "Logic_LogFile.log";

	public Logic(Storage s, History h) throws IOException {
		storage = s;
		history = h;
		tasks = storage.getTasks();
		logger = Logger.getLogger(Logic.class.getName());
		FileHandler logicFileHandler = new FileHandler(LOGIC_LOG_FILE, false);
		logger.addHandler(logicFileHandler);
		logicFileHandler.setFormatter(new SimpleFormatter());
	}

	public Result processUserInput(String userInput) throws IOException,
			ParseException, ClassNotFoundException {
		Result result = executeCommand(userInput);
		return result;
	}

	enum CommandType {
		DISPLAY, ADD, DELETE, EDIT, SAVE, EXIT, INVALID, SEARCH, DONE, UNDONE, UNDO, REDO, HISTORY, EXPORT, IMPORT
	};

	private CommandType determineCommandType(String command) {

		try {
			if (command == null)
				;
		} catch (NullPointerException e) {
			e.printStackTrace();
		}
		if (command.equalsIgnoreCase(COMMAND_ADD)
				|| command.equalsIgnoreCase(COMMAND_CREATE)) {
			return CommandType.ADD;
		} else if (command.equalsIgnoreCase(COMMAND_DISPLAY)
				|| command.equalsIgnoreCase(COMMAND_SHOW)) {
			return CommandType.DISPLAY;
		} else if (command.equalsIgnoreCase(COMMAND_SEARCH)
				|| command.equalsIgnoreCase(COMMAND_FIND)) {
			return CommandType.SEARCH;
		} else if (command.equalsIgnoreCase(COMMAND_DELETE)
				|| command.equalsIgnoreCase(COMMAND_DEL)) {
			return CommandType.DELETE;
		} else if (command.equalsIgnoreCase(COMMAND_EDIT)) {
			return CommandType.EDIT;
		} else if (command.equalsIgnoreCase(COMMAND_DONE)) {
			return CommandType.DONE;
		} else if (command.equalsIgnoreCase(COMMAND_UNDONE)) {
			return CommandType.UNDONE;
		} else if (command.equalsIgnoreCase(COMMAND_UNDO)) {
			return CommandType.UNDO;
		} else if (command.equalsIgnoreCase(COMMAND_HISTORY)) {
			return CommandType.HISTORY;
		} else if (command.equalsIgnoreCase(COMMAND_EXPORT)) {
			return CommandType.EXPORT;
		} else if (command.equalsIgnoreCase(COMMAND_IMPORT)) {
			return CommandType.IMPORT;
		} else if (command.equalsIgnoreCase(COMMAND_REDO)) {
			return CommandType.REDO;
		} else if (command.equalsIgnoreCase(COMMAND_SAVE)) {
			return CommandType.SAVE;
		} else if (command.equalsIgnoreCase(COMMAND_EXIT)
				|| command.equalsIgnoreCase(COMMAND_QUIT)) {
			return CommandType.EXIT;
		} else {
			return CommandType.INVALID;
		}
	}

	private Result executeCommand(String userInput) throws IOException,
			ClassNotFoundException {
		String command = getUserCommand(userInput);
		String data = getUserData(userInput);
		Result result = null;

		CommandType commandType = determineCommandType(command);

		switch (commandType) {
		case ADD:
			result = add(data);
			break;
		case DISPLAY:
			result = display(userInput);
			break;
		case SEARCH:
			result = search(data);
			break;
		case DELETE:
			result = delete(data);
			break;
		case EDIT:
			result = edit(data);
			break;
		case DONE:
			result = done(data);
			break;
		case UNDONE:
			result = undone(data);
			break;
		case UNDO:
			result = undo();
			break;
		case HISTORY:
			result = showHistory();
			break;
		case EXPORT:
			result = getExport(data);
			break;
		case IMPORT:
			result = getImport(data);
			break;
		case REDO:
			result = redo();
			break;
		case SAVE:
			result = save();
			break;
		case EXIT:
			saveExit();
			System.exit(0);
			break;
		case INVALID:
			result = new Result(ERRORMSG_INVALID_COMMAND,
					ERRORMSG_INVALID_COMMAND, true);
			break;
		default:
			result = new Result(EMPTY, DEFAULT_CONDITION, true);
		}
		return result;
	}

	private String obtainFirstWord(String sentence) {

		String[] words;
		String trimmedSentence;
		trimmedSentence = sentence.trim();

		words = trimmedSentence.split(SPACING);

		return words[0];

	}

	private String getUserCommand(String userInput) {
		return obtainFirstWord(userInput);
	}

	private String getUserData(String userInput) {
		return omitFirstWord(userInput);
	}

	private String omitFirstWord(String sentence) {

		String[] words;
		String trimmedSentence;
		trimmedSentence = sentence.trim();

		words = trimmedSentence.split(SPACING, 2);
		if (words.length == 2) {
			return words[1];
		} else {
			return EMPTY;
		}

	}
	//@author A0080413U 
	private Result save() throws IOException {
		String output = storage.writeToStorage(tasks);
		Result result = new Result(output, "SAVE", false);
		return result;

	}
	//@author A0100976E
	private Result edit(String data) throws IOException {
		String outputToUser;
		EditCommand editCommand;
		editCommand = new EditCommand(tasks, storage, history, logger, true);
		editCommand.acceptInput(data);
		editCommand.execute();
		editCommand.save();
		outputToUser = editCommand.outputToUser();
		Result result = new Result(outputToUser, COMMAND_EDIT,
				editCommand.isError());

		return result;

	}

	private Result delete(String data) throws IOException {
		String outputToUser;

		DeleteCommand deleteCommand = new DeleteCommand(tasks, storage,
				history, logger);
		deleteCommand.acceptInput(data);
		deleteCommand.execute();
		deleteCommand.save();
		outputToUser = deleteCommand.outputToUser();
		Result result = new Result(outputToUser, COMMAND_DELETE,
				deleteCommand.isError());
		return result;

	}

	private Result search(String data) {
		String outputToUser;
		SearchCommand searchCommand = new SearchCommand(tasks, storage,
				history, logger);
		searchCommand.acceptInput(data);
		searchCommand.execute();
		outputToUser = searchCommand.outputToUser();
		Result result = new Result(outputToUser, COMMAND_SEARCH,
				searchCommand.isError());
		return result;

	}

	private Result done(String data) throws IOException {

		String outputToUser;
		DoneCommand doneCommand = new DoneCommand(tasks, storage, history,
				logger);
		doneCommand.acceptInput(data);
		doneCommand.execute();
		doneCommand.save();
		outputToUser = doneCommand.outputToUser();
		Result result = new Result(outputToUser, COMMAND_DONE,
				doneCommand.isError());
		return result;

	}

	private Result undone(String data) throws IOException {
		String outputToUser;
		UndoneCommand undoneCommand = new UndoneCommand(tasks, storage,
				history, logger);
		undoneCommand.acceptInput(data);
		undoneCommand.execute();
		undoneCommand.save();
		outputToUser = undoneCommand.outputToUser();
		Result result = new Result(outputToUser, COMMAND_UNDONE,
				undoneCommand.isError());
		return result;

	}

	private Result display(String data) {
		String outputToUser;
		DisplayCommand displayCommand = new DisplayCommand(tasks, storage,
				history, logger);
		displayCommand.acceptInput(data);
		displayCommand.execute();
		outputToUser = displayCommand.outputToUser();
		Result result = new Result(outputToUser, COMMAND_DISPLAY,
				displayCommand.isError());
		return result;

	}

	//@author A0105546J

	private Result undo() {

		UndoCommand undoCommand = new UndoCommand(history);

		UndoAndRedoResult undoResult = undoCommand.execute(tasks);

		if (undoResult.getTasks() == null) {

			// do nothing.

		} else {
			tasks = undoResult.getTasks();

		}

		return undoResult.getResult();

	}

	private Result redo() {

		RedoCommand redoCommand = new RedoCommand(history);

		UndoAndRedoResult undoAndRedoResult = redoCommand.execute(tasks);

		if (undoAndRedoResult.getTasks() == null) {

			// do nothing.

		} else {
			tasks = undoAndRedoResult.getTasks();

		}

		return undoAndRedoResult.getResult();

	}

	private Result showHistory() {

		ShowHistory showHistory = new ShowHistory(history);

		String outputToUser;

		outputToUser = showHistory.execute();

		Result result = new Result(outputToUser, COMMAND_HISTORY, false);
		return result;

	}

	// @author A0100976E
	private Result add(String data) throws IOException {
		String outputToUser;

		AddCommand addCommand = new AddCommand(tasks, storage, history, logger);
		addCommand.acceptInput(data);
		addCommand.execute();
		addCommand.save();
		outputToUser = addCommand.outputToUser();
		Result result = new Result(outputToUser, COMMAND_ADD,
				addCommand.isError());
		if (addCommand.getWarning()) {
			result.setWarning(true);
			result.setWarningMessage(addCommand.getWarningMessage());
		}
		return result;

	}
	//@author A0080413U
	private void saveExit() throws IOException {
		storage.writeToStorage(tasks);
		storage.writeToRecovery(tasks);
	}
	//@author A0100976E
	public int getNumOfTasksToday() {

		DateTime todayDate = new DateTime();
		SearchCommand searchCommand = new SearchCommand(tasks, storage,
				history, logger);
		System.out.println(getContentOfTasksToday());
		return searchCommand.getNumOfTasksToday(todayDate);
	}

	public String getContentOfTasksToday() {
		return search(TODAY).getOutput();
	}
	//@author A0080413U
	public String getHelp() throws IOException {
		return storage.getHelp();
	}

	public Result getExport(String data) throws IOException {
		boolean exportSuccess;
		String outputToUser;
		exportSuccess = storage.exportTaskFile(data);
		if (exportSuccess) {
			outputToUser = EXPORT_SUCCESS;
		} else {
			outputToUser = EXPORT_FAIL;
		}

		Result result = new Result(outputToUser, COMMAND_EXPORT, false);
		return result;
	}

	public Result getImport(String data) throws ClassNotFoundException,
			IOException {
		boolean importSuccess;
		String outputToUser;
		importSuccess = storage.importTaskFile(data);
		if (importSuccess) {
			outputToUser = IMPORT_SUCCESS;
		} else {
			outputToUser = IMPORT_FAIL;
		}

		Result result = new Result(outputToUser, COMMAND_EXPORT, false);
		tasks = storage.getTasks();
		return result;
	}

}
